package com.yyaccp.jdbc.core;

import com.yyaccp.jdbc.util.ConfigManager;
import com.yyaccp.jdbc.util.DataSourceUtils;

import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;

public class JdbcTemplate {
    public int executeUpdate(String sql, Object[] args) {
        return (int) execute(sql, args, pstmt -> pstmt.executeUpdate());
    }

    public <T> T queryForObject(String sql, Object[] args, RowMapper<T> rowMapper) {
        List<T> list = queryForList(sql, args, rowMapper);
        if (list.isEmpty()) {
            return null;
        } else {
            return list.get(0);
        }
    }

    public <T> List<T> queryForList(String sql, Object[] args, RowMapper<T> rowMapper) {
        final List<T> resultList = new ArrayList<>();
        this.execute(sql, args, pstmt -> {
            ResultSet resultSet = pstmt.executeQuery();
            while (resultSet.next()) {
                resultList.add(rowMapper.mapRow(resultSet));
            }
            DataSourceUtils.close(resultSet);
            return null;
        });
        return resultList;
    }

    public <T> PageResult<T> queryForPage(String sql, Object[] args, RowMapper<T> rowMapper, int pageNo, int pageSize) {
        PageResult<T> pageResult = new PageResult<>();
        String countSql = "select count(1) "  + sql.substring(sql.toLowerCase().indexOf("from "));
        int totalCount = this.queryForObject(countSql, args, resultSet -> resultSet.getInt(1));
        if(totalCount == 0) return pageResult;
        if(pageNo < 1) pageNo = 1;
        if(pageSize < 1) pageSize = PageResult.PAGE_SIZE;
        pageResult.setPageNo(pageNo);
        pageResult.setPageSize(pageSize);
        pageResult.setTotalCount(totalCount);
        pageResult.setTotalPageCount((int)Math.ceil((double)totalCount / pageSize));
        if(pageNo > pageResult.getTotalPageCount()) {
            pageResult.setPageNo(pageResult.getTotalPageCount());
        }
        sql += " limit " + (pageResult.getPageNo() - 1) * pageResult.getPageSize() + ", " + pageResult.getPageSize();
        List<T> resultList = this.queryForList(sql, args, rowMapper);
        pageResult.setDataList(resultList);
        return  pageResult;
    }

    private Object execute(String sql, Object[] args, PreparedStatementCallback callback) {
        Object result = null;
        Connection connection = null;
        PreparedStatement preparedStatement = null;
        try {
            connection = DataSourceUtils.getConnection();
            preparedStatement = connection.prepareStatement(sql);
            bindArgs(preparedStatement, args);
            if("true".equals(ConfigManager.getInstance().getValue("showSql"))) {
                System.out.println(sql);
                System.out.println(Arrays.deepToString(args));
            }
            result = callback.doInPreparedStatement(preparedStatement);
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            DataSourceUtils.closeAll(null, preparedStatement, connection);
        }
        return result;
    }

    private void bindArgs(PreparedStatement preparedStatement, Object[] args) throws SQLException {
        if(args != null) {
            for (int i = 0; i < args.length; i++) {
                preparedStatement.setObject(i + 1, args[i]);
            }
        }
    }
}
